package com.evolved.automata.experimental.tests;
import com.evolved.automata.AITools;
import com.evolved.automata.WeightedValue;
import com.evolved.automata.experimental.*;
import com.evolved.automata.experimental.bots.*;
import com.evolved.automata.experimental.tools.*;
import java.io.*;
import java.util.LinkedList;
import java.util.concurrent.LinkedBlockingQueue;

public class ProviderTests implements ConsoleLineInputReader {
	
	private enum Mode
	{
		SRV1,AGENT
	}
	
	Mode currentMode;
	
	SRV1SensoryMotorProvider j_SRV1;
	private static String j_BaseInputPath;
	ProbabilisticTrainingAgent j_CurrentAgent;
	ProviderTests me;
	boolean j_ContinueAgentTestingP;
	LinkedBlockingQueue<Object> controlPipe;
	
	
	SubAgent autoTrainer = new SubAgent()
	{

		private final int MOVING=0;
		private final int ROTATING=1;
		private int j_ForwardCount=0;
		private int j_RotationCount=0;
		private int j_BehaviorMode=MOVING;
		private int j_LastRotation=SRV1Robot.LEFT;
		
		public int SelectAction(int[] state, UtilityAndPerferenceModule.Utility utility,LinkedList<WeightedValue<Integer>> actionOptions)
		{
			int baseDistance = state[0];
			if (baseDistance  == -1)
			{
				if (Math.random()*100>50)
					return SRV1Robot.BACKWARD_LEFT;
				else
					return SRV1Robot.BACKWARD_RIGHT;
			}
			else
			{
				switch (j_BehaviorMode)
				{
					case MOVING:
						if (baseDistance<6)
						{
							j_RotationCount = (int)(100*Math.random())+10;
							j_BehaviorMode=ROTATING;
							if (Math.random()>.5)
								j_LastRotation = SRV1Robot.LEFT;
							else
								j_LastRotation = SRV1Robot.RIGHT;
						}
						else
						{
							if (j_ForwardCount>0)
							{
								j_ForwardCount--;
							}
							else
							{
								if (Math.random()>.5)
								{
									j_ForwardCount = 20 + (int)(Math.random()*50);
								}
								else
								{
									j_RotationCount = (int)(50*Math.random())+10;
									j_BehaviorMode=ROTATING;
									if (Math.random()>.5)
										j_LastRotation = SRV1Robot.LEFT;
									else
										j_LastRotation = SRV1Robot.RIGHT;
									return j_LastRotation;
								}
							}
						}
						return SRV1Robot.FORWARD;
					case ROTATING:
						if (baseDistance<6)
						{
							j_RotationCount = (int)(100*Math.random())+10;
							if (Math.random()>.5)
								j_LastRotation = SRV1Robot.LEFT;
							else
								j_LastRotation = SRV1Robot.RIGHT;
						}
						else
						{
							if (j_RotationCount<0)
							{
								j_BehaviorMode=MOVING;
								j_ForwardCount = 20 + (int)(Math.random()*50);
								return SRV1Robot.FORWARD;
							}
							else
							{
								j_RotationCount--;
							}
						}
						return j_LastRotation;
				}
				return j_LastRotation;
				
			}	
		}
	};
	
	
	SubAgent baseAgent = new SubAgent()
	{
		private WeightedValue<Integer> j_LastAction=null;
		private WeightedValue<Integer> j_ForwardAction=new WeightedValue<Integer>(new Integer(SRV1Robot.FORWARD),0);
		
		public int SelectAction(int[] state, UtilityAndPerferenceModule.Utility utility,LinkedList<WeightedValue<Integer>> actionOptions)
		{
			int[][] view=null;
			view = AITools.DistribViewer(actionOptions);
			boolean validActionsP=false;
			int baseDistance = state[0];
			if (baseDistance  == -1)
			{
				if (Math.random()*100>50)
					return SRV1Robot.BACKWARD_LEFT;
				else
					return SRV1Robot.BACKWARD_RIGHT;
			}
			else
			{
				LinkedList<WeightedValue<Integer>> overideList = new LinkedList<WeightedValue<Integer>>();
				overideList.add(new WeightedValue<Integer>(new Integer(SRV1Robot.STOP),0));
				actionOptions=AITools.ListOveride(actionOptions, overideList);
				WeightedValue<Integer> previousValue=null;
				if ((previousValue=AITools.FindValueInList(actionOptions, j_ForwardAction))!=null)
				{
					if (previousValue.GetWeight()>0)
						return previousValue.GetValue().intValue();
				}
				
				if (j_LastAction!=null)
				{
					if ((previousValue=AITools.FindValueInList(actionOptions, j_LastAction))!=null)
					{
						if (previousValue.GetWeight()>0)
							return previousValue.GetValue().intValue();
					}
				}
				for (WeightedValue<Integer> choice:actionOptions)
				{
					if (choice.GetWeight()>0)
					{
						validActionsP=true;
						break;
					}
				}
				if (validActionsP)
				{
					j_LastAction = AITools.ChooseWeightedRandomFair(actionOptions);
					return j_LastAction.GetValue().intValue();
				}
				else
					return SRV1Robot.STOP;
				
				
			}	
		}
	};
	
	public ProviderTests()
	{
		
		me = this;
		j_ContinueAgentTestingP=true;
	}
	
	
	// 
	public static void main(String[] args)
	{
		// 1
		//SRV1SensoryMotorProvider.EnableDebugMode();
		if ((args!=null)&&(args.length>0))
		{
			// 2
			j_BaseInputPath=args[0];
		}
		else
		{
			// 3
			j_BaseInputPath=null;
		}
			
		
		ProviderTests tester;
		try
		{
			tester = new ProviderTests();
			tester.BeginTests();
		}
		catch (Exception e)
		{
			// 4
			System.out.println(e.toString());
			System.exit(1);
		}
		System.exit(0);
	}
	
	public void BeginTests()
	{
		// 1
		//TestSRV1Provider();
		//TestSRV1Agent();
		//TestSRV1MimicingAgent();
		TestSRV1AvoidanceAgent();
	}
	
	
	
	
	private void TestSRV1AvoidanceAgent()
	{
		// 1
		StaticTools.TraceLog("Beginning Avoidance test trace log");
		EvolutionaryEnvironment.DisplayTextLine("Beginning Agent Avoidance Mode");
		currentMode = Mode.AGENT;
		String agentName = "AvoidanceAgent";
		
		boolean loadFromFileP=true;
		UtilityAndPerferenceModule utilityInterpreter;
		SensoryMotorDataProvider provider;
		String uniqueName;
		uniqueName= "ProbabilisticTrainingAgent."+agentName;
		provider = new SRV1SensoryMotorProvider();
		
		UtilityListener defaultListener = new UtilityListener()
		{
			public void UpdateUtility(String agentShort, UtilityAndPerferenceModule.Utility utility)
			{
				
			}
		};
		SRV1SensoryMotorProvider tempProvider = new SRV1SensoryMotorProvider(true);
		//tempProvider.EnableDebugMode();
		int initialPreviousDistance = tempProvider.GetSensoryData()[0];
		int stateBufferSize=320;
		int minimumDistance=10;
		
		utilityInterpreter = new SimpleAvoidancePreference(defaultListener ,uniqueName,minimumDistance,initialPreviousDistance);
		
		
		
		
		
		if (loadFromFileP)
		{
			
			j_CurrentAgent = new ProbabilisticTrainingAgent();
			j_CurrentAgent.Load(j_BaseInputPath+agentName);
			j_CurrentAgent.SetName(uniqueName);
			// This only needs to be called if the agent is loaded from a file
			j_CurrentAgent.SetSensoryMotorProvider(provider);
			j_CurrentAgent.SetUtilityListener(defaultListener);
			j_CurrentAgent.SetSubAgent(baseAgent);
			j_CurrentAgent.SetSubAgentOverideStatus(false);
		}
		else
		{
			int actionHistoryLength=2, stateHistoryLength=2, stateActionQueueLength=4;
			int initialAction=SRV1Robot.STOP;
			j_CurrentAgent = new ProbabilisticTrainingAgent(
					uniqueName,
					provider,
					utilityInterpreter,
					actionHistoryLength,
					stateHistoryLength,
					stateActionQueueLength,
					initialAction,
					stateBufferSize);
			j_CurrentAgent.SetName(uniqueName);
			j_CurrentAgent.SetSubAgent(autoTrainer);
			j_CurrentAgent.ClearActionHistoryAfterStim();
			j_CurrentAgent.SetSubAgentOverideStatus(true);
		}
		
		
		
		BeginInterpreter();
		AgentInfo info = new AgentInfo(provider,j_CurrentAgent);
		
		while (j_ContinueAgentTestingP)
		{
			info.UpdateAgent();
			//AITools.IncrementAll(baseActions, 1); Not doing this yet
		}
		String detailedDataFileFullName="E:\\AI_system\\blog\\agent_tests\\learned_avoidance_data.txt";
		j_CurrentAgent.WriteBehaviorDetailsToFile(detailedDataFileFullName, ProbabilisticTrainingAgent.BehaviorDistributions.LEARNED_NEGATIVE);
		if (!loadFromFileP)
			j_CurrentAgent.Save(j_BaseInputPath+agentName);
	}
	
	
	
	private void TestSRV1Provider()
	{
		j_SRV1 = new SRV1SensoryMotorProvider();
		currentMode = Mode.SRV1;
		SimpleConsoleInterpreter interpreter;
		interpreter = new  SimpleConsoleInterpreter(this);
		
		EvolutionaryEnvironment.DisplayTextLine("Starting SRV1 Info Console.\n");
		interpreter.Start();
	}

	private void BeginInterpreter()
	{
		Thread consoleThread;
		Runnable readConsole = new Runnable()
		{
			public void run()
			{
				SimpleConsoleInterpreter interpreter;
				interpreter = new  SimpleConsoleInterpreter(me);
				interpreter.Start();
			}
		};
		consoleThread = new Thread(readConsole);
		consoleThread.start();
	}
	@Override
	public boolean ProcessLine(String lineinput) {
		switch (currentMode)
		{
			case AGENT:
				return ProcessAgentMode(lineinput);
			case SRV1:
				return ProcessSimpleSRV1Mode(lineinput);
		}
		return false;
	}
	
	private boolean ProcessSimpleSRV1Mode(String lineinput)
	{
		// TODO Auto-generated method stub
		int[] action = new int[1], data;
		String aname="";
		int steps=1;
		String[] parts;
		String command="";
		if (lineinput.length()>0)
		{
			parts = lineinput.split(" ");
			if (parts.length==2)
			{
				
				steps = Integer.parseInt(parts[1]);
			}

			command=parts[0];
			if (command.equalsIgnoreCase("i"))
			{
				action[0]=SRV1Robot.FORWARD;
				aname = "forward";
				
				
			} else if (command.equalsIgnoreCase("k"))
			{
				
				action[0]=SRV1Robot.BACK;
				aname = "back";
			}
			else if (command.equalsIgnoreCase("j"))
			{
				
				action[0]=SRV1Robot.LEFT;
				aname = "left";
			}
			else if (command.equalsIgnoreCase("l"))
			{
				
				action[0]=SRV1Robot.RIGHT;
				aname = "right";
			}
			
			while (steps>0)
			{
				EvolutionaryEnvironment.DisplayTextLine("Moving " + aname + " for " + steps+ " remaining step(s)");
				j_SRV1.ApplyAction(action);
				steps--;
				data = j_SRV1.GetSensoryData();
				if (data!=null)
					EvolutionaryEnvironment.DisplayTextLine("New Distance is: " + data[0]);
				else
					EvolutionaryEnvironment.DisplayTextLine("Failure to get new distance");
			}
			
			
			return true;
		}
		EvolutionaryEnvironment.DisplayTextLine("Finished SRV1 tests");
		return false;
	}
	
	private boolean ProcessAgentMode(String lineinput)
	{
		// TODO Auto-generated method stub
		int[] action = new int[1], data;
		String aname="";
		int steps=1;
		String[] parts;
		String command="";
		action[0] = new Integer(SRV1Robot.STOP);
		if (lineinput.length()>0)
		{
			parts = lineinput.split(" ");
			if (parts.length==2)
			{
				
				steps = Integer.parseInt(parts[1]);
			}

			command=parts[0];
			if (command.equalsIgnoreCase("i"))
			{
				action[0]=SRV1Robot.FORWARD;
				aname = "forward";
				
				
			} else if (command.equalsIgnoreCase("k"))
			{
				
				action[0]=SRV1Robot.BACK;
				aname = "back";
			}
			else if (command.equalsIgnoreCase("j"))
			{
				
				action[0]=SRV1Robot.LEFT;
				aname = "left";
			}
			else if (command.equalsIgnoreCase("l"))
			{
				
				action[0]=SRV1Robot.RIGHT;
				aname = "right";
			}
			else if (command.equalsIgnoreCase("r")) // Autorun
			{
				
				action[0]=-1;
				aname = "auto";
			}
			else if (command.equalsIgnoreCase("t")) // auto-trainer
			{
				
				action[0]=-2;
				aname = "auto-trainer";
			}
			while (steps>0)
			{
				EvolutionaryEnvironment.DisplayTextLine("Moving " + aname + " for " + steps+ " remaining step(s)");
				j_CurrentAgent.SetActionExplicit(action[0]);
				steps--;
			}
			
			
			return true;
		}
		EvolutionaryEnvironment.DisplayTextLine("Finished SRV1 Agent tests");
		j_ContinueAgentTestingP=false;
		j_CurrentAgent.SetActionExplicit(action[0]);
		return false;
	}
	
}
